#include "lcd.h"
#include "log.h"


SPI_HandleTypeDef g_tSpiLcdHandle;
DMA_HandleTypeDef g_tLcdDmaTxHandle;
TIM_HandleTypeDef g_tLcdTim2;
//static SemaphoreHandle_t s_xSpiLcdMutex = NULL;
bool g_bEnableWriteFlag;


void LcdSpiInit(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    LCD_SPI_CLK_ENABLE();
    LCD_GPIO_CLK_ENABLE();

    GPIO_InitStruct.Mode      = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull      = GPIO_NOPULL;
    GPIO_InitStruct.Speed     = GPIO_SPEED_FAST;
    GPIO_InitStruct.Pin       = LCD_CS_PIN;
    HAL_GPIO_Init(LCD_CS_PORT, &GPIO_InitStruct);
    LCD_CS_1();

    GPIO_InitStruct.Pin = LCD_DC_PIN;
    HAL_GPIO_Init(LCD_DC_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LCD_RESET_PIN;
    HAL_GPIO_Init(LCD_RESET_PORT, &GPIO_InitStruct);
    
    GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Alternate = LCD_GPIO_AF;
    GPIO_InitStruct.Pin       = LCD_SCK_PIN;
    HAL_GPIO_Init(LCD_SCK_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LCD_MOSI_PIN;
    HAL_GPIO_Init(LCD_MOSI_PORT, &GPIO_InitStruct);

    g_tSpiLcdHandle.Instance               = LCD_SPI;
    g_tSpiLcdHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
    g_tSpiLcdHandle.Init.Direction         = SPI_DIRECTION_2LINES;
    g_tSpiLcdHandle.Init.CLKPhase          = SPI_PHASE_2EDGE;
    g_tSpiLcdHandle.Init.CLKPolarity       = SPI_POLARITY_HIGH;
    g_tSpiLcdHandle.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;
    g_tSpiLcdHandle.Init.CRCPolynomial     = 10;
    g_tSpiLcdHandle.Init.DataSize          = SPI_DATASIZE_8BIT;
    g_tSpiLcdHandle.Init.FirstBit          = SPI_FIRSTBIT_MSB;
    g_tSpiLcdHandle.Init.NSS               = SPI_NSS_SOFT;
    g_tSpiLcdHandle.Init.TIMode            = SPI_TIMODE_DISABLE;
    g_tSpiLcdHandle.Init.Mode              = SPI_MODE_MASTER;
    HAL_SPI_Init(&g_tSpiLcdHandle);
    __HAL_SPI_ENABLE(&g_tSpiLcdHandle);

    __HAL_RCC_DMA1_CLK_ENABLE();

    g_tLcdDmaTxHandle.Instance = DMA1_Stream5;
    g_tLcdDmaTxHandle.Init.Channel = DMA_CHANNEL_0;
    g_tLcdDmaTxHandle.Init.Direction = DMA_MEMORY_TO_PERIPH;
    g_tLcdDmaTxHandle.Init.PeriphInc = DMA_PINC_DISABLE;
    g_tLcdDmaTxHandle.Init.MemInc = DMA_MINC_ENABLE;
    g_tLcdDmaTxHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    g_tLcdDmaTxHandle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    g_tLcdDmaTxHandle.Init.Mode = DMA_NORMAL;
    g_tLcdDmaTxHandle.Init.Priority = DMA_PRIORITY_MEDIUM;
    g_tLcdDmaTxHandle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    HAL_DMA_Init(&g_tLcdDmaTxHandle);
    __HAL_LINKDMA(&g_tSpiLcdHandle, hdmatx, g_tLcdDmaTxHandle);

    HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 3, 0);
    HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
}

void LcdSpiDeInit(void)
{
    
}

void LcdSpiSendData(unsigned char _ucData)
{
    HAL_SPI_Transmit(&g_tSpiLcdHandle, &_ucData, 1, 50);
    while(HAL_SPI_GetState(&g_tSpiLcdHandle) != HAL_SPI_STATE_READY);
}

void LcdSpiSendData16(unsigned short _usData)
{
	LcdSpiSendData((_usData >> 8) & 0xFF);
	LcdSpiSendData((_usData >> 0) & 0xFF);
}

void LcdWriteCmd(unsigned char _ucCmd)
{
    LCD_DC_0();
    LcdSpiSendData(_ucCmd);
    LCD_DC_1();
}

void LcdWriteData(unsigned char _ucData)
{
    LcdSpiSendData(_ucData);
}

void LcdWriteData16(uint16 _usData)
{
    LcdSpiSendData16(_usData);
}

void LcdBackLightInit(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};
    TIM_OC_InitTypeDef sConfigOC = {0};
    
    LCD_BACKLIGHT_TIM_CLK_ENABLE();
    LCD_BACKLIGHT_GPIO_CLK_ENABLE();
    
    GPIO_InitStruct.Pin = LCD_BACKLIGHT_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
    HAL_GPIO_Init(LCD_BACKLIGHT_PORT, &GPIO_InitStruct);

    g_tLcdTim2.Instance = LCD_BACKLIGHT_TIMER;
    g_tLcdTim2.Init.Prescaler = LCD_BACKLIGHT_PRESCALE - 1;
    g_tLcdTim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    g_tLcdTim2.Init.Period = LCD_BACKLIGHT_PERIOD - 1;
    g_tLcdTim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    g_tLcdTim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    if(HAL_TIM_Base_Init(&g_tLcdTim2) != HAL_OK)
    {
        HAL_ERROR(0, 0);
    }
    
    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    if(HAL_TIM_ConfigClockSource(&g_tLcdTim2, &sClockSourceConfig) != HAL_OK)
    {
        HAL_ERROR(0, 0);
    }
    if(HAL_TIM_PWM_Init(&g_tLcdTim2) != HAL_OK)
    {
        HAL_ERROR(0, 0);
    }
    
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    if(HAL_TIMEx_MasterConfigSynchronization(&g_tLcdTim2, &sMasterConfig) != HAL_OK)
    {
        HAL_ERROR(0, 0);
    }
    
    sConfigOC.OCMode = TIM_OCMODE_PWM1;
    sConfigOC.Pulse = LCD_BACKLIGHT_PERIOD - 1;
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
    sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
    if(HAL_TIM_PWM_ConfigChannel(&g_tLcdTim2, &sConfigOC, LCD_BACKLIGHT_CHANNEL) != HAL_OK)
    {
        HAL_ERROR(0, 0);
    }

    HAL_TIM_PWM_Start(&g_tLcdTim2, LCD_BACKLIGHT_CHANNEL);
}

void LcdSetBackLight(uint32 _uiValue)
{
    uint32 uiValue = _uiValue;

    if(uiValue > LCD_BACKLIGHT_PERIOD)
    {
        uiValue = LCD_BACKLIGHT_PERIOD;
    }

    __HAL_TIM_SET_COMPARE(&g_tLcdTim2, LCD_BACKLIGHT_CHANNEL, uiValue);
}

void LcdInit(void)
{
    LcdBackLightInit();

    LcdSpiInit();

    LCD_CS_0();

    LCD_RESET_0();
    DwtDelayMs(100);
    LCD_RESET_1();
    DwtDelayMs(100);

    LcdWriteCmd(0x11);
    DwtDelayMs(120); 
    LcdWriteCmd(0x36); 
    LcdWriteData(0x70);

    LcdWriteCmd(0x3A);
    LcdWriteData(0x05);

    LcdWriteCmd(0xB2);
    LcdWriteData(0x0C);
    LcdWriteData(0x0C);
    LcdWriteData(0x00);
    LcdWriteData(0x33);
    LcdWriteData(0x33); 

    LcdWriteCmd(0xB7); 
    LcdWriteData(0x35);  

    LcdWriteCmd(0xBB);
    LcdWriteData(0x19);

    LcdWriteCmd(0xC0);
    LcdWriteData(0x2C);

    LcdWriteCmd(0xC2);
    LcdWriteData(0x01);

    LcdWriteCmd(0xC3);
    LcdWriteData(0x12);   

    LcdWriteCmd(0xC4);
    LcdWriteData(0x20);  

    LcdWriteCmd(0xC6); 
    LcdWriteData(0x0F);    

    LcdWriteCmd(0xD0); 
    LcdWriteData(0xA4);
    LcdWriteData(0xA1);

    LcdWriteCmd(0xE0);
    LcdWriteData(0xD0);
    LcdWriteData(0x04);
    LcdWriteData(0x0D);
    LcdWriteData(0x11);
    LcdWriteData(0x13);
    LcdWriteData(0x2B);
    LcdWriteData(0x3F);
    LcdWriteData(0x54);
    LcdWriteData(0x4C);
    LcdWriteData(0x18);
    LcdWriteData(0x0D);
    LcdWriteData(0x0B);
    LcdWriteData(0x1F);
    LcdWriteData(0x23);

    LcdWriteCmd(0xE1);
    LcdWriteData(0xD0);
    LcdWriteData(0x04);
    LcdWriteData(0x0C);
    LcdWriteData(0x11);
    LcdWriteData(0x13);
    LcdWriteData(0x2C);
    LcdWriteData(0x3F);
    LcdWriteData(0x44);
    LcdWriteData(0x51);
    LcdWriteData(0x2F);
    LcdWriteData(0x1F);
    LcdWriteData(0x1F);
    LcdWriteData(0x20);
    LcdWriteData(0x23);

    LcdWriteCmd(0x21); 

    LcdWriteCmd(0x29);

    LCD_CS_1();
}

void LcdDeInit(void)
{
    LcdSpiDeInit();
}

void LcdStartWrite(void)
{
	LCD_CS_0();
	g_bEnableWriteFlag = true;
}

void LcdEndWrite(void)
{
	LCD_CS_1();
	g_bEnableWriteFlag = false;
}

bool LcdSetAddress(uint16_t _usX, uint16_t _usY, uint16_t _usWidth, uint16_t _usHeight)
{
	uint16_t usXStart = _usX + 40;
	uint16_t usXEnd = usXStart + _usWidth - 1;
	uint16_t usYStart = _usY + 53;
	uint16_t usYEnd = usYStart + _usHeight - 1;

	if ((_usX > LCD_WIDTH) || ((_usX + _usWidth) > LCD_WIDTH))
	{
		return false;
	}

	if ((_usY > LCD_HEIGHT) || ((_usY + _usHeight) > LCD_HEIGHT))
	{
		return false;
	}

	LcdWriteCmd(0x2A);
	LcdWriteData16(usXStart);
	LcdWriteData16(usXEnd);
	
	LcdWriteCmd(0x2B);
	LcdWriteData16(usYStart);
	LcdWriteData16(usYEnd);
	
	LcdWriteCmd(0x2C);

	return true;
}


void LcdDrawPixels(uint16_t *_pusColor, uint16_t _usSize)
{
	const uint32_t uiMaxDmaSize = 1024;

	uint8_t aucBuf[uiMaxDmaSize] = {0};
	uint32_t uiByteSizeOfColor = 0;
	uint32_t i, uiColorBufAddress = 0;

	if(g_bEnableWriteFlag != true)
	{
	    return;
	}

	//LCD_CS_0();

	uiByteSizeOfColor = 2 * _usSize;

	while(uiByteSizeOfColor > uiMaxDmaSize)
	{
		for(i = 0; i < uiMaxDmaSize; i += 2)
		{
			aucBuf[i] = (_pusColor[i / 2 + uiColorBufAddress] >> 8) & 0xFF;
			aucBuf[i + 1] = (_pusColor[i / 2 + +uiColorBufAddress] >> 0) & 0xFF;
		}

		HAL_SPI_Transmit_DMA(&g_tSpiLcdHandle, aucBuf, uiMaxDmaSize);
		while(HAL_SPI_GetState(&g_tSpiLcdHandle) != HAL_SPI_STATE_READY);

		uiByteSizeOfColor -= uiMaxDmaSize;
		uiColorBufAddress += uiMaxDmaSize / 2;
	}

	for(i = 0; i < uiByteSizeOfColor; i += 2)
	{
		aucBuf[i] = (_pusColor[i / 2 + uiColorBufAddress] >> 8) & 0xFF;
		aucBuf[i + 1] = (_pusColor[i / 2 + uiColorBufAddress] >> 0) & 0xFF;
	}

	HAL_SPI_Transmit_DMA(&g_tSpiLcdHandle, aucBuf, uiByteSizeOfColor);
	while(HAL_SPI_GetState(&g_tSpiLcdHandle) != HAL_SPI_STATE_READY);
	
	//LCD_CS_1();
}

void LcdFillScreen(uint16 _usColor)
{
    uint16_t usX = 0, usY = 0;
    uint16_t usColorBuf[LCD_WIDTH];

    LcdStartWrite();

	if(g_bEnableWriteFlag != true)
	{
	    return;
	}

	for(usX = 0; usX < LCD_WIDTH; usX++)
	{
		usColorBuf[usX] = _usColor;
	}
   
    for(usY = 0; usY < LCD_HEIGHT; usY++)
    {
		LcdSetAddress(0, usY, LCD_WIDTH, 1);
        LcdDrawPixels(usColorBuf, LCD_WIDTH);
    }

    LcdEndWrite();
}

void DMA1_Stream5_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&g_tLcdDmaTxHandle);
}


